/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package hxg.http.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import org.apache.http.HttpException;
import org.apache.http.HttpInetConnection;
import org.apache.http.HttpRequest;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.impl.nio.DefaultHttpServerIODispatch;
import org.apache.http.impl.nio.DefaultNHttpServerConnection;
import org.apache.http.impl.nio.DefaultNHttpServerConnectionFactory;
import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.nio.NHttpConnectionFactory;
import org.apache.http.nio.NHttpServerConnection;
import org.apache.http.nio.protocol.HttpAsyncRequestHandler;
import org.apache.http.nio.protocol.HttpAsyncRequestHandlerMapper;
import org.apache.http.nio.protocol.HttpAsyncService;
import org.apache.http.nio.protocol.UriHttpAsyncRequestHandlerMapper;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.reactor.IOReactorException;
import org.apache.http.nio.reactor.IOReactorStatus;
import org.apache.http.nio.reactor.ListeningIOReactor;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpProcessorBuilder;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.apache.http.protocol.UriPatternMatcher;

/**
 *
 * @author hstel-hxg
 */
final public class HttpServer {

    int port;
    ListeningIOReactor ioReactor;
    IOEventDispatch ioEventDispatch;
    UriHttpAsyncRequestHandlerMapper reqistry = new UriHttpAsyncRequestHandlerMapper();
    static transient SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss SSS");

    public HttpServer(int port) throws IOReactorException {
        this.port = port;
        HttpProcessor httpproc = HttpProcessorBuilder.create()
                .add(new ResponseDate())
                .add(new ResponseServer("Test/1.1"))
                .add(new ResponseContent())
                .add(new ResponseConnControl()).build();
        // Register the default handler for all URIs
        HttpAsyncService protocolHandler = new HttpAsyncService(httpproc, reqistry) {

            @Override
            public void requestReceived(NHttpServerConnection conn) throws IOException, HttpException {
                RequestLogger.log(System.currentTimeMillis() + ","
                        + sdf.format(new Date()) + ","
                        + conn.getHttpRequest().getRequestLine() + ","
                        + ((HttpInetConnection) conn.getContext().getAttribute("http.connection")).getRemoteAddress().getHostAddress() + '\n');
                super.requestReceived(conn); //To change body of generated methods, choose Tools | Templates.
            }

        };
        NHttpConnectionFactory<DefaultNHttpServerConnection> connFactory = new DefaultNHttpServerConnectionFactory(
                ConnectionConfig.DEFAULT);
        // Create server-side I/O event dispatch
        ioEventDispatch = new DefaultHttpServerIODispatch(protocolHandler, connFactory);
        // Set I/O reactor defaults
        IOReactorConfig config = IOReactorConfig.custom()
                .setIoThreadCount(1)
                .setSoTimeout(3000)
                .setConnectTimeout(3000)
                .build();
        // Create server-side I/O reactor
        ioReactor = new DefaultListeningIOReactor(config);
    }

    public HttpServer() throws IOReactorException {
        this(80);
    }

    public HttpServer(int port, NamedRequestHandler... handlers) throws IOReactorException {
        this(80);
        Arrays.stream(handlers).forEach((handler)->{
            register(handler);
        });
    }
    
    

    public final void register(String pattern, HttpAsyncRequestHandler<HttpRequest> handler) {
        reqistry.register(pattern, handler);
    }

    public final void unregister(String pattern) {
        reqistry.unregister(pattern);
    }

    /**
     *
     * @param handler
     */
    public final void register(NamedRequestHandler handler) {
        HttpPath path = handler.getClass().getAnnotation(HttpPath.class);
        register(path == null
                ? handler.getName().startsWith("/")
                        ? handler.getName()
                        : "/" + handler.getName()
                : path.name(), handler);
    }

    public final int getPort() {
        return port;
    }

    public final void setPort(int port) {
        //仅在未执行时可以设置端口
        if (ioReactor.getStatus() == IOReactorStatus.INACTIVE) {
            this.port = port;
        }
    }

    public void start() throws IOException {
        if (ioReactor.getStatus() == IOReactorStatus.INACTIVE) {
            ioReactor.listen(new InetSocketAddress(port));
            ioReactor.execute(ioEventDispatch);
        }
    }

    public void pause() throws IOException {
        if (ioReactor.getStatus() == IOReactorStatus.ACTIVE) {
            ioReactor.pause();
        }
    }

    public void resume() throws IOException {
        if (ioReactor.getStatus() == IOReactorStatus.INACTIVE) {
            ioReactor.resume();
        }
    }

}
